#include <iostream>
#include <stdint.h>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <mach/mach.h>
#include <mach/mach_time.h>

#include <rs/Assert.hpp>
#include <rs/Atomic.hpp>
#include <rs/MetaFacilities.hpp>
#include <rs/Portability.hpp>
#include <rs/math/Matrix4x4.hpp>
#include <rs/math/Quaternion.hpp>
#include <rs/reflection/Reflection.hpp>

using namespace rs;
using namespace rs::meta;

uint32_t swap_words( uint32_t arg )
{
    uint16_t* const sp = reinterpret_cast< uint16_t* >(&arg);
    uint16_t        hi = sp[0];
    uint16_t        lo = sp[1];

    sp[1] = hi;
    sp[0] = lo;

    return (arg);
} 

static unsigned int convertmantissa(unsigned int i) {
	unsigned int m = i<<13;// Zero pad mantissa bits
	unsigned int e = 0x38800000;// Exponent with adjusted bias ((127-14)<<23)
	while (!(m & 0x00800000)) {// While not normalized
		e -= 0x00800000;// Decrement exponent (1<<23)
		m <<= 1;// Shift mantissa
	}
	m &= ~0x00800000;// Clear leading 1 bit
	return m | e;// Return combined number
}

void PrintHalfFloatTables() {
    static unsigned int mantissatable[2048];
	static unsigned int exponenttable[64];
	static unsigned short offsettable[64];
	static unsigned short basetable[512];
	static unsigned char shifttable[512];
	
	mantissatable[0] = 0;
	for (int i=1; i<1024; ++i)
		mantissatable[i] = convertmantissa(i);
	for (int i=1024; i<2048; ++i)
		mantissatable[i] = 0x38000000 + ((i-1024)<<13);

	exponenttable[0] = 0;
	for (int i=1; i<31; ++i)
		exponenttable[i] = i<<23;
	exponenttable[31] = 0x47800000;
	exponenttable[32] = 0x80000000;
	for (int i=33; i<63; ++i)
		exponenttable[i] = 0x80000000 + ((i-32)<<23);
	exponenttable[63] = 0xC7800000;

	offsettable[0] = 0;
	for (int i=1; i<32; ++i)
		offsettable[i] = 1024;
	offsettable[32] = 0;
	for (int i=33; i<64; ++i)
		offsettable[i] = 1024;

	for (int i=0; i<256; ++i) {
		int e = i-127;
		if (e < -24) { // Very small numbers map to zero
			basetable[i|0x000] = 0x0000;
			basetable[i|0x100] = 0x8000;
			shifttable[i|0x000] = 24;
			shifttable[i|0x100] = 24;
		} else if (e < -14) { // Small numbers map to denorms
			basetable[i|0x000] = static_cast<unsigned short>(0x0400>>(-e-14));
			basetable[i|0x100] = static_cast<unsigned short>((0x0400>>(-e-14)) | 0x8000);
			shifttable[i|0x000] = static_cast<unsigned char>(-e-1);
			shifttable[i|0x100] = static_cast<unsigned char>(-e-1);
		} else if (e < 16) { // Normal numbers just lose precision
			basetable[i|0x000] = static_cast<unsigned short>((e+15)<<10);
			basetable[i|0x100] = static_cast<unsigned short>(((e+15)<<10) | 0x8000);
			shifttable[i|0x000] = 13;
			shifttable[i|0x100] = 13;
		} else if (e < 128) { // Large numbers map to Infinity
			basetable[i|0x000] = 0x7C00;
			basetable[i|0x100] = 0xFC00;
			shifttable[i|0x000] = 24;
			shifttable[i|0x100] = 24;
		} else { // Infinity and NaN's stay Infinity and NaN's
			basetable[i|0x000] = 0x7C00;
			basetable[i|0x100] = 0xFC00;
			shifttable[i|0x000] = 13;
			shifttable[i|0x100] = 13;
		}
	}
	
	std::cout << "{\n";
    int newline = 1;
    for(int i = 0; i < 512; ++i) {
        if (newline == 0) {
            std::cout << "\t";
        }
        
        if (i == 511) {
            printf("0x%X", shifttable[i]);
        }
        else {
            printf("0x%X, ", shifttable[i]);
        }
        
        if (newline == 8) {
            std::cout << std::endl;
            newline = 0;
        }
        
        ++newline;
    }
    std::cout << "\n}\n";
}

RsSize GetMaxMipMapCount(RsSize width, RsSize height) {
    RS_ASSERT(width > 0, "'width' must be greater than zero");
    RS_ASSERT(height > 0, "'height' must be greater than zero");
    
    RsSize count = 0;
    while (width != 1 && height != 1) {
        width >>= 1;
        height >>= 1;
        ++count;
    }
    
    return count;
}

struct ConstructorTest
{
    RS_DECLARE_REFLECTION();
    
    ConstructorTest()
    : integer(0),
      decimal(0.0),
      string("empty") {
    }
    
    ConstructorTest(int i)
    : integer(i),
      decimal(0.0),
      string("empty") {
    }
    
    ConstructorTest(int* i)
    : integer(*i),
    decimal(0.0),
    string("empty") {
    }
    
    ConstructorTest(int i, double d)
    : integer(i),
      decimal(d),
      string("empty") {
    }
    
    ConstructorTest(int i, double d, std::string const& str)
    : integer(i),
      decimal(d),
      string(str) {
    }
    
    Int32 integer;
    Double decimal;
    std::string string;
};


RS_DEFINE_REFLECTION_CLASS(ConstructorTest)
    .BindConstructor< ConstructorTest >()
    .BindConstructor< ConstructorTest, Int32 >()
    .BindConstructor< ConstructorTest, Int32* >()
    .BindConstructor< ConstructorTest, Int32, Double >()
    .BindConstructor< ConstructorTest, Int32, Double, std::string >()
    .BindAttribute("integer", &ConstructorTest::integer)
    .BindAttribute("decimal", &ConstructorTest::decimal)
    .BindAttribute("string", &ConstructorTest::string);
    
Int64 GetTicks() {
    UInt64 now = mach_absolute_time();
    static mach_timebase_info_data_t timeInfo = { 0, 0 };
    mach_timebase_info(&timeInfo);
    
    if (timeInfo.denom == timeInfo.numer) {
        return static_cast< Int64 >(now);
    }
    else if (timeInfo.denom == 1) {
        return static_cast< Int64 >(now * timeInfo.numer);
    }
    else {
        UInt64 eta32 = now >> 32;
        UInt64 eps32 = now & 0x00000000FFFFFFFFLL;
        
        // form product of decomposed time and numerator
        UInt64 mu64 = timeInfo.numer * eta32;
        UInt64 lambda64 = timeInfo.numer * eps32;
        
        // divide the constituents by denominator
        UInt64 q32 = mu64 / timeInfo.denom;
        UInt64 r32 = mu64 - (q32 * timeInfo.denom);
        return (static_cast< Int64 >((q32 << 32) + ((r32 << 32) + lambda64) / timeInfo.denom));
    }
}

Double TicksToMicroseconds(Int64 ticks) {
    return ((ticks / static_cast< Double >(1000LL)));
}

Double TicksToMilliseconds(Int32 ticks) {
    return ((ticks / static_cast< Double >(1000000LL)));
}

Double TicksToSeconds(Int32 ticks) {
    return ((ticks / static_cast< Double >(1000000000LL)));
}
    
int main (int argc, char * const argv[]) {
#   if defined(RS_DEBUG)
    std::cout << "Debug\n";
#   elif defined(RS_TEST)
    std::cout << "Test\n";
#   elif defined(RS_RELEASE)
    std::cout << "Release\n";
#   endif
    
    // TYPES
    #if 0
    RS_ASSERT(sizeof(rs::Int8) == sizeof(int8_t), 0);
    RS_ASSERT(sizeof(rs::Int16) == sizeof(int16_t), 0);
    RS_ASSERT(sizeof(rs::Int32) == sizeof(int32_t), 0);
    RS_ASSERT(sizeof(rs::Int64) == sizeof(int64_t), 0);
    RS_ASSERT(sizeof(rs::IntPtr) == sizeof(intptr_t), 0);
    RS_ASSERT(sizeof(rs::RsSize) == sizeof(size_t), 0);

    Int32 volatile x = 1;
    RS_ASSERT(atomic::AddFetch(&x, -2) == -1, "atomic::AddFetch failed");
    std::cout << "successful" << std::endl;
    
    Int8 result = 0;
    BranchlessTernary(true, (Int8)1, (Int8)-1, result);
    RS_ASSERT(result == 1,  "BranchlessTernary failed");
    
    BranchlessTernary(false, (Int8)1, (Int8)-1, result);
    RS_ASSERT(result == -1,  "BranchlessTernary failed");
    
    UInt8 result1 = 0;
    BranchlessTernary(true, (UInt8)1, (UInt8)2, result1);
    RS_ASSERT(result1 == 1,  "BranchlessTernary failed");
    
    std::cout << GetMaxMipMapCount(512, 512) << std::endl;
    
    PrintHalfFloatTables();
    #endif
    
    Int64 start, end;
    rs::reflection::Class const& classRecord = rs::reflection::ReflectionCenter::GetReflectionCenter().GetClassRecord("ConstructorTest");
    start = GetTicks();
    ConstructorTest* instance = static_cast< ConstructorTest* >(classRecord.Construct(5, 10.0));
    end = GetTicks();
    std::cout << "Reflection Construct Time (microseconds): " << TicksToMicroseconds(end) - TicksToMicroseconds(start) << std::endl;
    delete instance;
    
    start = GetTicks();
    ConstructorTest* instance2 = new ConstructorTest(5, 10.0);
    end = GetTicks();
    std::cout << "Normal Construct Time (microseconds): " << TicksToMicroseconds(end) - TicksToMicroseconds(start) << std::endl;
    delete instance2;
    
    return 0;
}
